
//
// Copyright 2009 Arne Halvorsen (arne.halvorsen@gmail.com)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

// Project page: http://code.google.com/p/mf4j/

//
// 091116 - aha42 - Starts in line change tracking.
// 091121 - aha42 - Added project page link (google code) to header comment.
// 091210a- aha42 - IOException -> FlickrException.
// 091210b- aha42 - Fixed bug that error message from Flickr not forwarded to
//                  thrown FlickrResponseException but the string 'msg'!
// 091210c- aha42 - Fixed so parse does not wrap FlickrException from yet
//                  another FlickrException exception.
// 091215 - aha42 - parse methods now accepts chkResponse parameter.
//

package org.aha.mf4j.response;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;

import org.aha.mf4j.FlickrException;
import org.xml.sax.SAXException;

/**
 * <p>
 *   Utility methods to parse a Flickr API XML response.
 * </p>
 * @author Arne Halvorsen (aha42)
 */
public final class FlickrResponseParser 
{
  // Utility pattern dictates private constructor.
  private FlickrResponseParser(){}
  
  /**
   * <p>
   *   Parse using passed 
   *   {@link FlickrXmlHandler}.
   * </p>
   * @param res         Response XML to parse.
   * @param chkResponse {@code true} if to check response XML and throw a 
   *                    {@link FlickrResponseException} if Flickr returns XML
   *                    where the {@code rsp} top level element has an 
   *                    {@code stat} attribute value different from 
   *                    {@code "ok"}, {@code false} if not to check. 
   * @return {@link FlickrObject} that top level xml element {@code rsp} is
   *         parsed into, root of parsed hierarchy.
   * @throws FlickrException If communication fails or Flickr reports an error.
   */
  public static FlickrObject parse(String res, boolean chkResponse)
    throws FlickrException
  {
    return parse(res, new FlickrXmlHandler(), chkResponse);
  }
  
  /**
   * <p>
   *   Parse using passed 
   *   {@link FlickrXmlHandler}.
   * </p>
   * @param res         Response XML to parse.
   * @param fh          Handler to use.
   * @param chkResponse {@code true} if to check response XML and throw a 
   *                    {@link FlickrResponseException} if Flickr returns XML
   *                    where the {@code rsp} top level element has an 
   *                    {@code stat} attribute value different from 
   *                    {@code "ok"}, {@code false} if not to check. 
   * @return {@link FlickrObject} that top level xml element {@code rsp} is
   *         parsed into, root of parsed hierarchy.
   * @throws FlickrException If communication fails or Flickr reports an error.
   */
  public static FlickrObject parse(String res, FlickrXmlHandler fh, 
    boolean chkResponse) throws FlickrException
  {
    if (res==null)
    {
      throw new NullPointerException("res");
    }
    if (fh==null)
    {
      throw new NullPointerException("fh");
    }
    
    fh.init();
    
    SAXParserFactory f=SAXParserFactory.newInstance();
    f.setNamespaceAware(false);
    f.setValidating(false);
    try
    {
      ByteArrayInputStream bais=null;
      
      try
      {
        bais=new ByteArrayInputStream(res.getBytes("UTF-8"));
      }
      catch (UnsupportedEncodingException uex)
      {
        IllegalStateException iax=new IllegalStateException();
        iax.initCause(uex);
        throw iax;
      }
      
      f.newSAXParser().parse(bais, fh);
      
      FlickrObject retVal=fh.getRoot();
      
      if (chkResponse) checkResponse(retVal);
      
      return retVal;
    }
    catch (FlickrException fx)
    {
      throw fx;
    }
    catch (IOException iox)
    {
      throw new FlickrException(iox);
    }
    catch (ParserConfigurationException pcx)
    {
      FlickrException fx=new FlickrException("ParserConfigurationException");
      fx.initCause(pcx);
      throw fx;
    }
    catch (SAXException saxx)
    {
      FlickrException fx=new FlickrException(saxx.getMessage());
      fx.initCause(saxx);
      throw fx;
    }
  }
  
  private static void checkResponse(FlickrObject res) throws FlickrException
  {
    if (res==null)
    {
      throw new NullPointerException("res");
    }
    
    String type=res.getType();
    
    if (type.equals("rsp"))
    {
      String stat=res.getAttribute("stat");
      if (stat!=null && stat.equals("ok")) return;
      
      FlickrObject err=res.getChild("err");
      
      String code=err.getAttribute("code");
      code=(code==null) ? "-1000" : code;
      
      String msg=err.getAttribute("msg");
      msg=(msg==null) ? "-" : msg;
      
      throw new FlickrResponseException(code, msg);
    }
    
    throw new FlickrException("expected type res when got "+type);
  }
  
}
